﻿// <copyright file="MeshGeometry3D.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Collections.Generic;
    using Axelerate.Silverlight3D.Controls;
    
    /// <summary>
    /// File name: MeshGeometry3D.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 10/13/2008 9:11:00 h.
    /// Description: Triangle primitive for building a 3-D shape.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    public sealed class MeshGeometry3D : Geometry3D
    {
        #region Fields
        
        /// <summary>
        /// public static readonly DependencyProperty NormalsProperty
        /// </summary>
        public static readonly DependencyProperty NormalsProperty =
                    DependencyProperty.Register(
                          "Normals",
                          typeof(Vector3DCollection),
                          typeof(MeshGeometry3D),
                          new PropertyMetadata(OnNormalsPropertyChanged));

        /// <summary>
        /// Identifies the Positions dependency property.
        /// </summary>
        public static readonly DependencyProperty PositionsProperty =
                    DependencyProperty.Register(
                          "Positions",
                          typeof(Point3DCollection),
                          typeof(MeshGeometry3D),
                          new PropertyMetadata(OnPositionsPropertyChanged));

        /// <summary>
        /// Identifies the TextureCoordinates dependency property.
        /// </summary>
        public static readonly DependencyProperty TextureCoordinatesProperty =
                    DependencyProperty.Register(
                          "TextureCoordinates",
                          typeof(PointCollection),
                          typeof(MeshGeometry3D),
                          new PropertyMetadata(OnTextureCoordinatesPropertyChanged));

        /// <summary>
        /// Identifies the TriangleIndices dependency property.
        /// </summary>
        public static readonly DependencyProperty TriangleIndicesProperty =
                    DependencyProperty.Register(
                          "TriangleIndices",
                          typeof(Int32Collection),
                          typeof(MeshGeometry3D),
                          new PropertyMetadata(OnTriangleIndicesPropertyChanged));

        /// <summary>
        /// Keep a cache copy of the render entities that represent on 2D this Mesh
        /// </summary>
        internal List<RenderEntity> _cachedRenderEntities = new List<RenderEntity>();

        private Rect3D _bounds;

        #endregion

        #region Events

        // this code is for future use in case i can inherit the Frezable class from DependencyObject instead of FrameworkElement
        ////// FOR FUTURE USE
        /////// <summary>
        /////// FOR FUTURE USE
        /////// </summary>
        ////public MouseEventHandler MouseLeave;
        
        ////// FOR FUTURE USE
        /////// <summary>
        /////// FOR FUTURE USE
        /////// </summary>
        ////public MouseEventHandler MouseEnter;
        
        ////// FOR FUTURE USE
        /////// <summary>
        /////// FOR FUTURE USE
        /////// </summary>
        ////public MouseButtonEventHandler MouseLeftButtonDown;
        
        ////// FOR FUTURE USE
        /////// <summary>
        /////// FOR FUTURE USE
        /////// </summary>
        ////public MouseButtonEventHandler MouseLeftButtonUp;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the bounding Rect3D for this MeshGeometry3D.
        /// </summary>
        public override Rect3D Bounds
        {
            get
            {
                return _bounds; // not well supported yet
            }
        }

        /// <summary>
        /// Gets or sets a collection of normal vectors for the MeshGeometry3D. This is a dependency property.
        /// </summary>
        public Vector3DCollection Normals
        {
            get
            {
                return (Vector3DCollection)this.GetValue(NormalsProperty);
            }
            set
            {
                Vector3DCollection OldValue = (Vector3DCollection)this.GetValue(NormalsProperty);
                if (OldValue != null)
                {
                    OldValue.CollectionChanged -= NormalsValue_CollectionChanged;
                }
                this.SetValue(NormalsProperty, value);
                value.CollectionChanged += NormalsValue_CollectionChanged;
            }
        }

        /// <summary>
        /// Notify when the collection of normals change internally.
        /// </summary>
        /// <param name="sender">object that send the event</param>
        /// <param name="e">event arguments with the information of the change</param>
        void NormalsValue_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.NotifyPropertyChanged("Normals");
        }

        /// <summary>
        /// Gets or sets a collection of vertex positions for a MeshGeometry3D. This is a dependency property.
        /// </summary>
        public Point3DCollection Positions
        {
            get
            {
                return (Point3DCollection)this.GetValue(PositionsProperty);
            }
            set
            {
                Point3DCollection OldValue = (Point3DCollection)this.GetValue(PositionsProperty);
                if (OldValue != null)
                {
                    OldValue.CollectionChanged -= PositionsValue_CollectionChanged;
                }
                this.SetValue(PositionsProperty, value);
                value.CollectionChanged += PositionsValue_CollectionChanged;
            }
        }

        /// <summary>
        /// Notify when the collection of Positions change internally.
        /// </summary>
        /// <param name="sender">object that send the event</param>
        /// <param name="e">event arguments with the information of the change</param>
        void PositionsValue_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.NotifyPropertyChanged("Normals");
        }

        /// <summary>
        /// Gets or sets a collection of texture coordinates for the MeshGeometry3D. This is a dependency property.
        /// </summary>
        public PointCollection TextureCoordinates
        {
            get
            {
                return (PointCollection)this.GetValue(TextureCoordinatesProperty);
            }
            set
            {
                this.SetValue(TextureCoordinatesProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a collection of entity indices for the MeshGeometry3D. This is a dependency property
        /// </summary>
        public Int32Collection TriangleIndices
        {
            get
            {
                return (Int32Collection)this.GetValue(TriangleIndicesProperty);
            }
            set
            {
                Int32Collection OldValue = (Int32Collection)this.GetValue(TriangleIndicesProperty);
                if (OldValue != null)
                {
                    OldValue.CollectionChanged -= TriangleIndicesValue_CollectionChanged;
                }
                this.SetValue(TriangleIndicesProperty, value);
                value.CollectionChanged += TriangleIndicesValue_CollectionChanged;
            }
        }

        /// <summary>
        /// Notify when the collection of TriangleIndices change internally.
        /// </summary>
        /// <param name="sender">object that send the event</param>
        /// <param name="e">event arguments with the information of the change</param>
        void TriangleIndicesValue_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.NotifyPropertyChanged("TriangleIndices");
        }

        #endregion

        #region Constructor
        public MeshGeometry3D()
            : base()
        {
        }
        #endregion

        #region Methods

        /// <summary>
        /// Raise the Normals changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnNormalsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((MeshGeometry3D)d).NotifyPropertyChanged("Normals");
        }

        /// <summary>
        /// Raise the Positions changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnPositionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((MeshGeometry3D)d).NotifyPropertyChanged("Positions");
        }

        /// <summary>
        /// Raise the TextureCoordinates changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnTextureCoordinatesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((MeshGeometry3D)d).NotifyPropertyChanged("TextureCoordinates");
        }

        /// <summary>
        /// Raise the TriangleIndices changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnTriangleIndicesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((MeshGeometry3D)d).NotifyPropertyChanged("TriangleIndices");
        }

        #endregion

        /// <summary>
        /// Fired when a entity of this mesh recieve a MouseLeftButtopUp event
        /// </summary>
        /// <param name="sender">Triangle that send the event</param>
        /// <param name="e">Event arguments</param>
        void entity_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            ////if (this.MouseLeftButtonUp != null)
            ////{
            ////    this.MouseLeftButtonUp(this, e);
            ////}
        }

        /// <summary>
        /// Fired when a entity of this mesh recieve a MouseLeftButtonDown event
        /// </summary>
        /// <param name="sender">Triangle that send the event</param>
        /// <param name="e">Event arguments</param>
        void entity_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            ////if (this.MouseLeftButtonDown != null)
            ////{
            ////    this.MouseLeftButtonDown(this, e);
            ////}
        }

        // for future use.. this event can be alot heavy for that reason i don't want at it.
        /////// <summary>
        /////// Fired when a entity of this mesh recieve a MouseMove event
        /////// </summary>
        /////// <param name="sender">Triangle that send the event</param>
        /////// <param name="e">Event arguments</param>
        ////void entity_MouseMove(object sender, MouseEventArgs e)
        ////{
        ////    //throw new NotImplementedException();
        ////}

        /// <summary>
        /// Fired when a entity of this mesh recieve a MouseLeave event
        /// </summary>
        /// <param name="sender">Triangle that send the event</param>
        /// <param name="e">Event arguments</param>
        void entity_MouseLeave(object sender, MouseEventArgs e)
        {
        }

        void entity_MouseEnter(object sender, MouseEventArgs e)
        {
        }

        /// <summary>
        /// Return the list of render ententies, but if the mesh is dirty require to regenerate the render entities and return a empty collection.
        /// </summary>
        internal override List<RenderEntity> RenderEntenties
        {
            get
            {
                return this._cachedRenderEntities;
            }
        }

        internal override void CreateRenderEntities(RenderEntityPool entityPool, Material material, Material backMaterial)
        {
            if (this.mIsDirty)
            {
                this._cachedRenderEntities.Clear();
                _bounds = new Rect3D();
                for (int entityIndex = 0; entityIndex < this.TriangleIndices.Count; entityIndex += 3)
                {
                    
                    if ((entityIndex + 2) <= (this.TriangleIndices.Count - 1))
                    {
                        RenderEntity t = entityPool.GetRenderEntity(this, this.TriangleIndices[entityIndex],
                                          this.TriangleIndices[entityIndex + 1],
                                          this.TriangleIndices[entityIndex + 2],
                                          material,
                                          backMaterial);
                        _bounds.Union(this.Positions[TriangleIndices[entityIndex]]);
                        _bounds.Union(this.Positions[TriangleIndices[entityIndex + 1]]);
                        _bounds.Union(this.Positions[TriangleIndices[entityIndex + 2]]);
                        // Add this eventhandlers is VERY SLOW.. maybe use hit testing is better way to detect mouseenter, mouseleave and mouse click
                        ////t.Visual.MouseEnter += new MouseEventHandler(entity_MouseEnter);
                        ////t.Visual.MouseLeave += new MouseEventHandler(entity_MouseLeave);

                        // mouse move is only useful to implement dragdrop of the elements. i am not sure that is necesary and can be very heavy to implement it.
                        ////entity.MouseMove += new MouseEventHandler(entity_MouseMove);

                        ////t.Visual.MouseLeftButtonDown += new MouseButtonEventHandler(entity_MouseLeftButtonDown);
                        ////t.Visual.MouseLeftButtonUp += new MouseButtonEventHandler(entity_MouseLeftButtonUp);

                        this.RenderEntenties.Add(t);
                    }
                }
            }
        }

    }
}
